﻿using System.Text.Json;
using System.Text.Json.Serialization;
using FluentValidation;
using FluentValidation.Results;
using FluentValidationExample.Constants;
using Microsoft.AspNetCore.Mvc;

namespace FluentValidationExample.Middlewares;

public class GlobalExceptionHandlerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionHandlerMiddleware> _logger;

    public GlobalExceptionHandlerMiddleware(RequestDelegate next, ILogger<GlobalExceptionHandlerMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        try
        {
            await _next(httpContext);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "发生了异常：{Message}", ex.Message);

            var problemDetails = GetExceptionDetails(ex);
            problemDetails.TraceId = httpContext.TraceIdentifier;

            httpContext.Response.StatusCode = problemDetails.Status!.Value;

            await httpContext.Response.WriteAsJsonAsync(problemDetails,
                new JsonSerializerOptions(JsonSerializerDefaults.Web), "application/problem+json");
        }
    }

    private GlobalExceptionProblemDetails GetExceptionDetails(Exception ex)
    {
        return ex switch
        {
            ValidationException validationException => new GlobalExceptionProblemDetails(validationException.Errors)
            {
                Status = StatusCodes.Status400BadRequest,
                Type = ExceptionConst.ValidationFailureType,
                Title = ExceptionConst.ValidationFailureTitle,
                Detail = ExceptionConst.ValidationFailureDetail,
            },
            _ => new GlobalExceptionProblemDetails()
            {
                Status = StatusCodes.Status500InternalServerError,
                Type = ExceptionConst.ServerErrorType,
                Title = ExceptionConst.ServerErrorTitle,
                Detail = ExceptionConst.ServerErrorDetail
            }
        };
    }
}

public sealed class GlobalExceptionProblemDetails : ProblemDetails
{
    public GlobalExceptionProblemDetails()
    {
    }

    public GlobalExceptionProblemDetails(IEnumerable<ValidationFailure> errors)
    {
        Errors = errors;
    }

    [JsonPropertyName("errors")]
    [JsonPropertyOrder(int.MaxValue)]
    public IEnumerable<ValidationFailure> Errors { get; } = new List<ValidationFailure>();

    [JsonPropertyName("TraceId")]
    [JsonPropertyOrder(int.MaxValue - 1)]
    public string? TraceId { get; set; }
}